#ifndef SERIAL_SURPPORT_H
#define SERIAL_SURPPORT_H

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/type_traits.hpp>
#include "scope_array.h"
using namespace std;

namespace serial
{
	class SizeCaculator;

	template<typename T>
	size_t size(const T& ) { return sizeof(T); }

	template< template<typename X, typename U = allocator<X> > class Container, typename T >
	size_t size(const Container<T>& i)
	{
		size_t ret = sizeof(typename Container<T>::size_type);
		for(typename Container<T>::const_iterator itr = i.begin(); itr != i.end(); ++itr)
		{
			ret += size((*itr));
		}
		return ret;
	}

	template<typename T>
	size_t size(const scoped_array<T>& array)
	{
		T t;
		return (size(t) * array.size() + 4);
	}

	template< template<typename Key, typename Value, typename = less<Key>, typename U = allocator<pair<const Key, Value> > > class Container, typename Key, typename Value>
	size_t size(const Container<Key, Value>& i)
	{
		size_t ret = sizeof(typename Container<Key, Value>::size_type);
		for(typename Container<Key, Value>::const_iterator itr = i.begin(); itr != i.end(); ++itr)
		{
			ret += size((*itr).first);
			ret += size((*itr).second);
		}
		return ret;
	}

	size_t size(const string& i);


	class SizeCaculator
	{
		size_t sz_;
	public:
		SizeCaculator() : sz_(0) {}

		size_t size() const
		{
			return sz_;
		}

		template<typename T>
		SizeCaculator& operator&(const T& t) 
		{
			sz_ += serial::size(t);
			return *this;
		}
	};


	template<typename T>
	void write(ostream& is, const T& i) { is.write((const char*)(&i), sizeof(T)); }

	template<typename T>
	void write(ostream& is, const scoped_array<T>& i, typename boost::enable_if<boost::is_class<T> >::type* = 0)
	{
		write(is, i.size());
		for(const T* t = i.get(); t != i.get() + i.size(); t++)
		{
			write(is, *t);
		}
	}

	template<typename T>
	void write(ostream& is, const scoped_array<T>& i, typename boost::disable_if<boost::is_class<T> >::type* = 0)
	{
		write(is, i.size());
		is.write((const char *)i.get(), sizeof(T) * i.size());
	}

	template< template<typename X, typename U = allocator<X> > class Container, typename T >
	void write(ostream& is, const Container<T>& i)
	{
		write(is, i.size());
		for(typename Container<T>::const_iterator itr = i.begin(); itr != i.end(); ++itr)
		{
			write(is, (*itr));
		}
	}

	template< template<typename Key, typename Value, typename = less<Key>, typename U = allocator<pair<const Key, Value> > > class Container, typename Key, typename Value>
	void write(ostream& is, const Container<Key, Value>& i)
	{
		write(is, i.size());
		for(typename Container<Key, Value>::const_iterator itr = i.begin(); itr != i.end(); ++itr)
		{
			write(is, (*itr).first);
			write(is, (*itr).second);
		}
	}

	void write(ostream& is, const string& i);

	/*template<typename T>
	class trait
	{
	public:
		typedef void type;
	};

	template<typename T>
	class trait<scoped_array<T> >
	{
	};*/

	template<typename T>
	void read(istream& is, T& i/*, typename trait<T>::type* = 0*/) { is.read((char*)(&i), sizeof(T));}


	template< template<typename U, typename X = allocator<U> > class Container, typename T >
	void read(istream& is, Container<T>& ctn)
	{
		size_t sz;
		read(is, sz);
		for(size_t i = 0; i < sz; i++)
		{
			T t;
			read(is, t);
			using namespace boost::assign;
			ctn += (typename Container<T>::value_type(t));
		}
	}

	template<typename T>
	void read(istream& is, scoped_array<T>& i, typename boost::enable_if<boost::is_class<T> >::type* = 0)
	{
		size_t sz;
		read(is, sz);
		i.reset(new T[sz], sz);
		for(size_t i = 0; i < sz; i++)
		{
			T t;
			read(is, t)
			i[i] = t;
		}
	}

	template<typename T>
	void read(istream& is, scoped_array<T>& i, typename boost::disable_if<boost::is_class<T> >::type* = 0)
	{
		size_t sz;
		read(is, sz);
		i.reset(new T[sz], sz);
		is.read((char*)i.get(), sz * sizeof(T));
	}


	template< template<typename Key, typename Value, typename = less<Key>, typename U = allocator<pair<const Key, Value> > > class Container, typename Key, typename Value>
	void read(istream& is, Container<Key, Value>& ctn)
	{
		size_t sz;
		read(is, sz);
		for(size_t i = 0; i < sz; i++)
		{
			typename Container<Key, Value>::value_type t;
			read(is, t.first);
			read(is, t.second);
			ctn.insert(t);
		}
	}


	void read(istream& is, string& i);


	template<typename T>
	ostream& operator &(ostream& os, const T& t)
	{
		write(os, t);
		return os;
	}


	template<typename T>
	istream& operator &(istream& os, T& t)
	{
		read(os, t);
		return os;
	}


	template<typename T>
	size_t serialSize(const T& t) { return sizeof(T); }
	size_t serialSize(const string& str);
}

#endif